/**********************************************************
FileName: poll.c
Author: Version : Date: Casiawu(wujh@emsyschina.com)
Description: // 网关 poll实现   2018.6.21    链表操作数据
1. -------
History: // 历史修改记
<author> <time> <version > <desc>  asasasa
casiawu 2008/2/12 1.0 build this module for linux (2.6.21)
***********************************************************/
#include <stdio.h>
#include <sys/types.h>  
#include <sys/stat.h>   
#include <fcntl.h>      
#include <errno.h>     
#include <string.h>
#include <stdlib.h>  
#include <string.h>  
#include <sys/epoll.h>  
#include <arpa/inet.h>  
#include <netinet/in.h>  
#include <unistd.h>  
#include <sys/socket.h>
#include <netinet/tcp.h> 
#include <dirent.h>
#include <signal.h>
#include <sys/wait.h>
#include <pthread.h>
#include <semaphore.h>

#include "link.h"
#include "gateway.h"
#include "thread_pool.h"

#define ERR_EXIT(m) \
do\
{\
    perror(m);\
    exit(EXIT_FAILURE);\
}\
while (0);\

#define 	ERR_NUM  120

void *ClientFun(void *argc);
void *ReadFun(void* argc);

void* PoolHandler(void *argc);

static int cli_fd = -1;
static Link   *head = NULL;
struct threadpool* pool = NULL;
static int  log_fd = -1;    //日志文件的文件描述符
static sem_t  sem;  //信号量

int main()
{
	int  epfd = -1;
	
	pthread_t  ser_rdpid;
	pthread_t  pid = 0;

	pthread_attr_t client_attr;   //线程属性
	
	//守护进程
	//if(daemon(0,0) == -1)
    //    ERR_EXIT("daemon error");
	
	log_fd = open("/mnt/nandflash/my.log",O_WRONLY|O_CREAT|O_APPEND,0644);
	if(log_fd == -1)
        ERR_EXIT("open error");
	
	//初始化线程属性
	pthread_attr_init(&client_attr);
	//设置分离线程属性
	pthread_attr_setdetachstate(&client_attr, PTHREAD_CREATE_DETACHED);

	//创建链表头
	head = CreateLinkHead();
	if(!head){
		printf("CreateLinkHead failed \n");
		return -1;
	}
	
	//创建epoll列表
	epfd = epoll_create(64);
	if(epfd < 0){  
		perror("epoll_create");  
		return -1; 
    }
	
	//初始化信号量
	if(sem_init(&sem, 0, 0) < 0){
		printf("sem_init failed \n");
		return -1;
	}
	pthread_create(&ser_rdpid, &client_attr, ClientFun, &epfd);	
	pthread_create(&pid, NULL, ReadFun, &epfd);
	
	while(1){
			
		delay(1,500);
	}
}

void *ReadFun(void* argc)
{
	int epfd = *(int *)argc;
	int ep_num;
	int getfd;
	int i;
	int ret;
	int rc = -1;
	char buf[64] = {0};
	struct epoll_event  events[64] = {0}; //用于回传要处理的事件
	Link   *node;
	LinkData  rddata = {0};
	int cnt = 0;
	
	int  sockfd = -1;
	int  connfd = -1;
	LinkData data = {0};

	pthread_detach(pthread_self());
	
	
	
	while(1){
		sem_wait(&sem);
		//创建服务器，端口 6666 
		sockfd = ServerInit("192.168.0.249", 6666);
		if(sockfd < 0){
			printf("ServerInit failed \n");
			close(sockfd);
			cnt ++;
			delay(0, 500);
			if(cnt == ERR_NUM){
				exit(0);
			}
			continue;
		}
		sprintf(buf, "ServerInit success, sockfd = %d \n",sockfd);
		write(log_fd, buf, strlen(buf));
		SetNonblockMode(sockfd);
		AddEvent(sockfd, epfd, EPOLLIN);
		
		while(1){
			if(cli_fd < 0){
				printf("%s : some error happen in server \n", __FUNCTION__);
				close(sockfd);
				break;
			}
			bzero(events, sizeof(events));
			ep_num = epoll_wait(epfd,events,32,-1);
			switch  (ep_num){
				case  0:  //超时
					//printf("timeout \n");
					break;
				case  -1:
					//错误
					perror("epoll_wait");
					//pthread_exit(0);
					break;
				default:
					for(i=0; i<ep_num; i++){
						getfd = events[i].data.fd;
						if(getfd == sockfd){
							//监听客户端
							connfd = accept(sockfd,NULL,NULL);//wait answer
							if(connfd<0){
								//perror("accept");
								close(connfd);
								delay(0, 50);
								continue;
								//
							}
							bzero(buf, sizeof(buf));
							sprintf(buf, "connect success fd = %d \n",connfd);
							write(log_fd, buf, strlen(buf));
							//设置keepalive心跳包
							SetTcpKeepalive(connfd, 20, 2, 3);
							SetNonblockMode(connfd);
							//将connfd添加到epoll列表中
							AddEvent(connfd, epfd, EPOLLIN | EPOLLOUT);
							bzero(&data, sizeof(data));
							data.fd = connfd;
							if(InsertLinkNode(head, data) < 0)
								printf("InsertLinkNode failed \n");
						}
						else if((events[i].events) & EPOLLIN){
							//客户端发来消息，读事件
							bzero(&rddata, sizeof(rddata));
							ret = ReadEvent(getfd, &rddata);
							switch  (ret){
								case  0:
									//threadpool_add_job(pool, PoolHandler, &rddata);
	
									//有客户端消息，先改变节点信息，若节点不存在，则创建节点
									//PrintTime("before search");
									//node = SearchLinkNode(head, getfd);
									//if(node){ 
									//	//节点存在，改变节点信息
									//	node->data = rddata;
									//}else{ 
									//	//节点不存在
									//}
									//send(rddata.fd, rddata.buf, rddata.len, 0);
									rc = send(cli_fd, &rddata, sizeof(rddata), 0);
									if(rc < 0){
										printf("send cli_fd failed , ret = %d\n",rc);
									}
									break;
								case -1:
									//客户端断开，删除节点并关闭连接套接字
									bzero(buf, sizeof(buf));
									sprintf(buf, "client end,fd = %d \n",getfd);
									write(log_fd, buf, strlen(buf));
									DeleteLinkNode(head, getfd);
									epoll_ctl(epfd, EPOLL_CTL_DEL, getfd, NULL);
									close(getfd);
									break;
								default :
									break;
							}
						}
					}
					break;
			}
		}
	}
}


/**--------------------------------------------------------------------------------
* 函数名称 :	ClientFun
* 功能描述 :	读服务器发回的数据
* 入口参数 :	void *argc   待传入的参数 其为epoll描述符
* 编辑作者 :	
* 返回值   ：   无
* 备  注   ：   如何通知线程 服务器已经掉线     全局cli_fd
---------------------------------------------------------------------------------*/
void *ClientFun(void *argc)
{
	int epfd = *(int *)argc;
	LinkData  rddata = {0};
	int ret = -1; 
	int i = 0;
	int maxfd;
	LinkData data = {0};
	fd_set readfs;
	struct timeval timeout={0, 100}; 
	char buf[128] = {0};
	
	pthread_detach(pthread_self());
	
	while(1){
		for(i=0; i<ERR_NUM; i++){
			cli_fd = ClientInit("192.168.0.100", 8888);
			if(cli_fd > 0){
				break;
			}
			delay(0,500);
		}
		if(i == ERR_NUM){
			printf("service did not run\n");
			write(log_fd, "service did not run\n", 64);
			exit(0);
		}
		printf("--------------------server up \n");
		write(log_fd, "--------------------server up \n", 120);
		
		sem_post(&sem);
		maxfd = cli_fd + 1;
		
		SetTcpKeepalive(cli_fd, 20, 2, 3);
		SetNonblockMode(cli_fd);
		
		while(1){
			
			if(cli_fd < 0){
				printf("%s : some error happen in server \n", __FUNCTION__);
				break;
			}
			
			FD_ZERO(&readfs);
			FD_SET(cli_fd,&readfs);
			
			ret = select(maxfd, &readfs, NULL, NULL, &timeout);
			if(ret>0){
				if(FD_ISSET(cli_fd,&readfs)){
					bzero(&rddata, sizeof(rddata));
					ret = read(cli_fd, &rddata, sizeof(rddata));
					if(ret <= 0){
						//服务器关闭，清空链表，并将文件描述符从列表中删除
						if(ClearLink(head, epfd)){
							printf("ClearLink failed \n");
						}
						bzero(buf, sizeof(buf));
						sprintf(buf, "***************************server down \n", 128);
						write(log_fd, buf, strlen(buf));
						epoll_ctl(epfd, EPOLL_CTL_DEL, cli_fd, NULL);
						close(cli_fd);
						cli_fd = -1;
						//pthread_exit(0);
					}else{
						//若在此之前节点已被删除，如何处理？
						write(rddata.fd, rddata.buf, rddata.len);
					}	
				}
			}
			
		}
	}
}


void *PoolHandler(void *argc)
{
	LinkData  rddata = *(LinkData *)argc;
	
	send(cli_fd, &rddata, sizeof(rddata), 0);
}
